using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Collections.Generic;
using MrDev.Graphics;
using MrDev.GameMath;
using MrDev.Physics;
using IrrKlang;

namespace MrDev.GameObjects
{
    /// <summary>
    /// Our fearless adventurer!
    /// </summary>
    class Player : GameObject
    {
        public List<Powerup> CollectedPowerups = new List<Powerup>();
        public event Level.BossScreenDelegate EnterBossScreen;
        public event Level.BossScreenDelegate ExitBossScreen;

        public int Score = 0;
        public bool GameOver = false;

        private Animation idleAnimationLeft;
        private Animation idleAnimationRight;
        private Animation runAnimation;
        private Animation jumpAnimation;
        private Animation celebrateAnimation;
        private Animation dieAnimation;
        private float shootCoolDown = 0;
        private bool exitBossScreen = true;

        /// <summary>
        /// Constructors a new player.
        /// </summary>
        public Player(Level level, Vector2 position) : base(level)
        {
            Parameters = PhysicsParameters.CreatePlayerParameters();
            Bullets = new List<Bullet>();
            LoadContent();
            Lives = 3;
            Score = 0;
            Parameters.RelativeCollisionRect = new Rectangle(12, 0, 24, 48);
            Reset(position);

            level.Physics.EnterBossScreen += new Level.BossScreenDelegate(Physics_EnterBossScreen);
            level.Physics.ExitBossScreen += new Level.BossScreenDelegate(Physics_ExitBossScreen);
        }

        void Physics_ExitBossScreen(GameObject sourceObject, int triggerTileX, int triggerTileY)
        {
            if (exitBossScreen || sourceObject != this)
                return;
            if (ExitBossScreen != null)
            {
                ExitBossScreen(sourceObject, triggerTileX, triggerTileY);
                exitBossScreen = true;
            }
        }

        void Physics_EnterBossScreen(GameObject sourceObject, int triggerTileX, int triggerTileY)
        {
            if (!exitBossScreen || sourceObject != this)
                return;
            if (EnterBossScreen != null)
            {
                EnterBossScreen(sourceObject, triggerTileX, triggerTileY);
                exitBossScreen = false;
            }
        }

        /// <summary>
        /// Loads the player sprite sheet and sounds.
        /// </summary>
        public void LoadContent()
        {
            // Load animated textures.
            idleAnimationLeft = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Idle"), 0.1f, true, 0,1);
            idleAnimationRight = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Idle"), 0.1f, true,1,1);
            idleAnimationRight.Texture.Name = "Debug";
            runAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Run"), 0.1f, true);
            jumpAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Jump"), 0.1f, false);
            celebrateAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Celebrate"), 0.1f, false);
            dieAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Die"), 0.1f, false);
        }

        /// <summary>
        /// Resets the player to life.
        /// </summary>
        /// <param name="position">The position to come to life at.</param>
        public void Reset(Vector2 position)
        {
            Level.FixedCamera = false;
            Level.Physics.SetObjectPosition(this, position);
            SetColor(Color.White);
            IsAlive = true;
            PlayAnimation(idleAnimationLeft);
            StopBlinkMode();
        }


        /// <summary>
        /// Handles input, performs physics, and animates the player sprite.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            if (IsAlive)
            {
                GetInput(gameTime);
                Level.Physics.ApplyPhysics(this, gameTime);
                Level.Physics.HandleCollisions(this);
            }



            List<Car> killedEnemies = new List<Car>();
            List<Bullet> killedBullets = new List<Bullet>();

            foreach (Bullet b in Bullets)
            {
                b.Update(gameTime);
                if (!b.IsAlive)
                    killedBullets.Add(b);
                else
                {
                    if (Level.Boss.IsAlive)
                    {
                        Vector2 collisionDepth = RectangleExtensions.GetIntersectionDepth(Level.Physics.AbsoluteCollisionRect(Level.Boss), Level.Physics.AbsoluteCollisionRect(b));
                        if (collisionDepth != Vector2.Zero)
                        {
                            b.IsAlive = false;
                            Level.Boss.Hit();
                            break;
                        }
                    }
                }

            }

            foreach (Bullet b in killedBullets)
            {
                Bullets.Remove(b);
            }

            foreach (Car enemy in Level.Enemies)
            {
                if (enemy.IsAlive)
                {
                    foreach (Bullet b in Bullets)
                    {
                        if (!b.IsAlive)
                            continue;
                        Vector2 collisionDepth = RectangleExtensions.GetIntersectionDepth(Level.Physics.AbsoluteCollisionRect(enemy), Level.Physics.AbsoluteCollisionRect(b));
                        if (collisionDepth != Vector2.Zero)
                        {
                            enemy.IsAlive = false;
                            Score += 1000;
                            b.IsAlive = false;
                            killedEnemies.Add(enemy);
                            Level.Explode(enemy.Parameters.Position);
                            break;
                        }
                    }
                }

                if (this.IsAlive && enemy.IsAlive)
                {
                    Vector2 collisionDepth = RectangleExtensions.GetIntersectionDepth(Level.Physics.AbsoluteCollisionRect(enemy), Level.Physics.AbsoluteCollisionRect(this));
                    if (collisionDepth != Vector2.Zero)
                    {
                        Kill();
                    }
                }
            }


            foreach (Car e in killedEnemies)
            {
                Level.Enemies.Remove(e);
            }


            // Collision with platforms
            foreach (Platform platform in Level.Platforms)
            {
                Rectangle platformBounds = Level.Physics.AbsoluteCollisionRect(platform);
                Rectangle playerBounds = Level.Physics.AbsoluteCollisionRect(this);
                Vector2 depth = RectangleExtensions.GetIntersectionDepth(playerBounds, platformBounds);
                if (depth != Vector2.Zero)
                {
                    if (Parameters.PreviousBounds.Bottom <= platform.Parameters.PreviousBounds.Top)
                    {
                        Level.Physics.StandOnAlien(this, platform);
                    }
                }
            }

            // Collision with powerups
            Powerup[] powerupArray = Level.Powerups.ToArray();
            for (int i = 0; i < powerupArray.Length; i++)
            {
                Powerup p = powerupArray[i];
                Rectangle powerupBounds = Level.Physics.AbsoluteCollisionRect(p);
                Rectangle playerBounds = Level.Physics.AbsoluteCollisionRect(this);
                Vector2 depth = RectangleExtensions.GetIntersectionDepth(playerBounds, powerupBounds);
                if (depth != Vector2.Zero)
                {
                    // Add to player and remove from level
                    CollectedPowerups.Add(new Powerup(Level, new Vector2(0, 0), p.PowerupType, 0));
                    PlatformerGame.Sound.Play2D("sound\\pickup.mp3");
                    Level.Powerups.Remove(p);
                }
            }


            // Clear input.
            Parameters.Movement = 0.0f;
        }

        public void Kill()
        {
            if (IsAlive == false)
                return;
            PlayAnimation(idleAnimationLeft);
            PlatformerGame.Sound.Play2D("sound\\die.mp3");
            //Parameters.Velocity.Y = Parameters.JumpVelocity;
            SetColor(Color.White);
            Lives--;
            IsAlive = false;
            if (Lives == 0)
                GameOver = true;
            Level.ExplodeInsane(Parameters.Position, 60, 50);
            StartBlinkMode(2f);
            

        }

        /// <summary>
        /// Gets player horizontal movement and jump commands from input.
        /// </summary>
        /// 
        private void GetInput(GameTime gameTime)
        {
            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
            // Get input state.
            KeyboardState keyboardState = Keyboard.GetState();

            // If any digital horizontal movement input is found, override the analog movement.
            if (keyboardState.IsKeyDown(Keys.Left) ||
                keyboardState.IsKeyDown(Keys.A))
            {
                PlayAnimation(runAnimation, SpriteEffects.None);
                Level.Physics.MoveLeft(this);
            }
            else if (keyboardState.IsKeyDown(Keys.Right) ||
                     keyboardState.IsKeyDown(Keys.D))
            {
                PlayAnimation(runAnimation, SpriteEffects.FlipHorizontally);
                Level.Physics.MoveRight(this);
            }

            // Check if the player wants to jump.
            if (keyboardState.IsKeyDown(Keys.Up) ||
                keyboardState.IsKeyDown(Keys.W))
            {
                Level.Physics.Jump(this);
            }
            else if (Parameters.IsJumping)
            {
                if (Parameters.Velocity.Y < 0)
                {
                    Parameters.Velocity.Y += 7000 * elapsedTime;
                    if (Parameters.Velocity.Y >= 0)
                        Parameters.Velocity.Y = 0;

                }
            }

            if (Math.Abs(Parameters.Velocity.X) < 10)
            {
                if(Parameters.FaceDirection == -1)
                    PlayAnimation(idleAnimationLeft, SpriteEffects.None);
                else
                    PlayAnimation(idleAnimationRight, SpriteEffects.None);
            }


            if (shootCoolDown > 0)
                shootCoolDown -= elapsedTime;
            else
                shootCoolDown = 0;

            // Check if the player wants to jump.
            if (keyboardState.IsKeyDown(Keys.LeftControl) ||
                keyboardState.IsKeyDown(Keys.RightControl))
            {
                if (shootCoolDown == 0)
                {
                    Shoot();
                    shootCoolDown = 0.1f;
                }
            }
        }

        private void Shoot()
        {
            if(!HasItem(Powerup.Powerups.Disc))
                return;
            Bullet b = new Bullet(Level, Parameters.Position - new Vector2(0, Parameters.SpriteSize.Height / 2));
            b.Parameters.Velocity = new Vector2(1, 0) * 800 * Parameters.FaceDirection;
            Bullets.Add(b);
            Level.SoundFXShot();

        }

        private bool HasItem(Powerup.Powerups powerups)
        {
            foreach (Powerup p in CollectedPowerups)
            {
                if (p.PowerupType == powerups)
                    return true;
            }
            return false;
        }

        public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            foreach (Bullet b in Bullets)
            {
                b.Draw(gameTime, spriteBatch);
            }

            base.Draw(gameTime, spriteBatch);
        }
    }
}
